<?php
/** 
 * 
 *
 * @package api
 * @subpackage collections
 * @author $Author$
 * @version $Id: AbstractSet.php 14 2006-11-28 22:05:19Z rdff3 $
 */
/**
 * 
 */
require_once( "lang/Object.php" );
require_once( "util/Collection.php" );
require_once( "util/AbstractCollection.php" );
require_once( "util/Set.php" );
/**
 * AbstractSet
 *
 * This class provides a skeletal implementation of the Set  interface to minimize the effort required to implement this interface.
 * 
 * The process of implementing a set by extending this class is identical to that of implementing a Collection by extending
 * AbstractCollection, except that all of the methods and constructors in subclasses of this class must obey the additional
 * constraints imposed by the Set interface (for instance, the add method must not permit addition of multiple instances of an 
 * object to a set).
 *
 * Note that this class does not override any of the implementations from the AbstractCollection class. It merely adds 
 * implementations for equals, hashCode, and removeAll.
 *
 * This class is a member of the PHP Collections Framework. 
 *
 * @package api
 * @subpackage collections
 * @author $Author$
 * @version $Id: AbstractSet.php 14 2006-11-28 22:05:19Z rdff3 $
 */
abstract class AbstractSet extends AbstractCollection implements Set {
	
	/**
	 * Compares the specified object with this set for equality.
	 *
	 * Compares the specified object with this set for equality. Returns true if the given object is also a set, the two sets 
	 * have the same size, and every member of the given set is contained in this set. This ensures that the equals method 
	 * works properly across different implementations of the Set  interface.
	 *
	 * This implementation first checks if the specified object is this set; if so it returns true. Then, it checks if the 
	 * specified object is a set whose size is identical to the size of this set; if not, it returns false. If so, it returns 
	 * containsAll((Collection) o). 
	 *
	 * @param Object $o Object to be compared for equality with this set.
	 * @returns boolean true if the specified object is equal to this set.
	 */
	public function equals( Object $o ) {
		if ($this === $o)
			return true;
		if ($this->size() != $o->size())
			return false;
		return $this->containsAll($o);
	}
	
	/**
	 * Returns the hash code value for this set.
	 *
	 * Returns the hash code value for this set. The hash code of a set is defined to be the sum of the hash codes of the elements 
	 * in the set. This ensures that s1.equals(s2) implies that s1.hashCode()==s2.hashCode() for any two sets s1  and s2, as required 
	 * by the general contract of Object.hashCode.
	 *
	 * This implementation enumerates over the set, calling the hashCode method on each element in the collection, and 
	 * adding up the results. 
	 *
	 * @returns int the hash code value fot his set.
	 */
	public function hashCode() {
		$ctx = hash_init("md5");
		
		$iterator = $this->iterator();
		while ($iterator->hasNext()) {
			hash_update( $ctx, $iterator->next()->hashCode() );
		}
		
		return hash_final($ctx);
	}
	
	/**
	 * Removes from this set all of its elements that are contained in the specified collection (optional operation).
	 *
	 * This implementation determines which is the smaller of this set and the specified collection, by invoking the size 
	 * method on each. If this set has fewer elements, then the implementation iterates over this set, checking each element 
	 * returned by the iterator in turn to see if it is contained in the specified collection. If it is so contained, it is 
	 * removed from this set with the iterator's remove method. If the specified collection has fewer elements, then the
	 * implementation iterates over the specified collection, removing from this set each element returned by the iterator, 
	 * using this set's remove method.
	 *
	 * Note that this implementation will throw an UnsupportedOperationException if the iterator returned by the iterator 
	 * method does not implement the remove method. 
	 *
	 * @param Collection $c elements to be removed from this set.
	 * @returns boolean true if this set changed as a result of the call.
	 * @throws {@link UnsupportedOperationException} - removeAll is not supported by this set. 
	 * @throws {@link NullPointerException} - if the specified collection is null.
	 */
	public function removeAll( Collection $c ) {
		if (is_null($c))
			throw new NullPointerException();
	
		$return_value = false;
		if ($c->size() < $this->size()) {
			$iterator = $c->iterator();
			while ($iterator->hasNext()) {
				$this->remove( $iterator->next() );
				$return_value = true;
			}
		}
		else {
			$iterator = $this->iterator();
			while ($iterator->hasNext() ) {
				$next = $iterator->next();
				if ( $c->contains( $next ) ) {
					$this->remove($next);
					$return_value = true;
				}
			}
		}
		return $return_value;
	}
	
}

?>
